# coding:utf-8
'''
Created on 2015年8月28日

@author: xjb
'''
import re
import bit

ISO8583_MAX_FIELD_IDX = 128

ISO8583_BITMAP_BIN = 0
ISO8583_BITMAP_HEX = 1
ISO8583_BITMAP_BCD = 2

# CONSTANTS (Fixed/Variable size indicators)
ISO8583_FIELD_SIZE_IND = {
    "ISO8583_FIXED" : 0,
    "ISO8583_LLVAR" : 2,
    "ISO8583_LLLVAR" : 3,
    "ISO8583_LLLLVAR" : 4,
    "ISO8583_CONTVAR" : 0
}

# CONSTANTS (Field Types)
ISO8583_FIELD_TYPES = {
    "ISO8583_N" : 0,
    "ISO8583_NS" : 1,
    "ISO8583_XN" : 2,
    "ISO8583_A" : 3,
    "ISO8583_AN" : 4,
    "ISO8583_ANS" : 5,
    "ISO8583_ANSB" : 6,
    "ISO8583_ANP" : 7,
    "ISO8583_B" : 8,
    "ISO8583_Z" : 9,
    "ISO8583_BMP" : 10
}
ISO8583_BMP = ISO8583_FIELD_TYPES["ISO8583_BMP"]

class Iso8583:
    def __init__(self, packager):
        self.packager = packager
        self.fld = [None for i in range(ISO8583_MAX_FIELD_IDX + 1)]
        self.bitmap = None
    
    def pack(self):
        self.bitmap = bytearray(ISO8583_MAX_FIELD_IDX / 8)  # reset bitmap
        
        data = []
        bitmap_field_idx = None
        bitmap_size = ISO8583_MAX_FIELD_IDX / 8
        secondary_bitmap = False
        i = 0
        while i <= ISO8583_MAX_FIELD_IDX:
            v = self.fld[i]
            
            var_len = self.packager.packager[i].var_len
            max_len = self.packager.packager[i].max_len
            
            if self.packager.packager[i].field_type == ISO8583_BMP:
                bitmap_field_idx = i
                data.append(None)  # 占位
            elif  v is not None:
                if var_len > 1:
                    data.append("%0*d%s" % (var_len, len(v), v))
                else:
                    data.append(v[0:max_len].ljust(max_len))
                
                if i >= 2:
                    bit.set_bit(self.bitmap, i - 1 , 1)
                    if i > ISO8583_MAX_FIELD_IDX / 2:
                        secondary_bitmap = True
            
            i = i + 1
        
        if bitmap_field_idx is not None:
            if secondary_bitmap:
                bit.set_bit(self.bitmap, 0 , 1)
            else:
                self.bitmap = self.bitmap[0:bitmap_size / 2]
            
            bitmap_mode = self.packager.packager[bitmap_field_idx].compress
            data[bitmap_field_idx] = self.__bytearray_to_bitmap(self.bitmap, bitmap_mode)
        else:
            raise Exception('Invalid bitmap field index')
        
        msg = "".join(data)
        
        return msg
                    
    
    def unpack(self, buf):
        self.fld = [None for i in range(ISO8583_MAX_FIELD_IDX + 1)]
        self.bitmap = bytearray(ISO8583_MAX_FIELD_IDX / 8)  # reset bitmap
        
        offset = 0
        max_field_idx = ISO8583_MAX_FIELD_IDX
        i = 0
        while i <= max_field_idx:
            var_len = self.packager.packager[i].var_len
            max_len = self.packager.packager[i].max_len
            
            if self.packager.packager[i].field_type == ISO8583_BMP:
                
                bitmap_size = ISO8583_MAX_FIELD_IDX / 8  # bitmap最长大小
                bitmap_mode = self.packager.packager[i].compress
                
                self.bitmap = self.__bitmap_to_bytearray(buf[offset:offset + 2], bitmap_mode)
                if bitmap_mode == ISO8583_BITMAP_HEX:
                    bitmap_size = bitmap_size * 2
                if bitmap_mode == ISO8583_BITMAP_BCD:
                    bitmap_size = bitmap_size * 2
                
                if bit.get_bit(self.bitmap, 0) == 0:
                    bitmap_size = bitmap_size / 2
                    max_field_idx = max_field_idx / 2
                    
                self.bitmap = self.__bitmap_to_bytearray(buf[offset:offset + bitmap_size], bitmap_mode)
                
                offset = offset + bitmap_size
       
            elif i == 0 or bit.get_bit(self.bitmap, i - 1) == 1:
                if var_len > 0:
                    max_len = int(buf[offset:offset + var_len])
                    offset = offset + var_len
                    self.fld[i] = buf[offset:offset + max_len]
                    offset = offset + max_len
                else:
                    self.fld[i] = buf[offset:offset + max_len]
                    offset = offset + max_len
            
            i = i + 1
    
    
    def __bitmap_to_bytearray(self, bitmap, bitmap_mode):
        if bitmap_mode == ISO8583_BITMAP_BCD:
            return self.__asc_to_bcd(bitmap)
        
        if bitmap_mode == ISO8583_BITMAP_HEX:
            bitmap = bitmap.decode("hex")
        
        return bytearray(bitmap)


    def __bytearray_to_bitmap(self, bitmap_bytes, bitmap_mode):
        if bitmap_mode == ISO8583_BITMAP_BCD:
            return self.__bcd_to_asc(bitmap_bytes)
        
        if bitmap_mode == ISO8583_BITMAP_HEX:
            return str(bitmap_bytes).encode("hex")
        
        return str(bitmap_bytes)


    def __asc_to_bcd(self, bitmap):
        bcd = bytearray()
        bitmap = bytearray(bitmap)
        
        for i in range(0, len(bitmap), 2):
            b = (bitmap[i] - 0x30) << 4 | (bitmap[i + 1] - 0x30)
            bcd.append(b)
            
        return bcd
    
    
    def __bcd_to_asc(self, bitmap):
        asc = bytearray()
        for i in range(len(bitmap)):
            b = ((bitmap[i] >> 4) & 0xf) + 0x30
            asc.append(b)
            b = (bitmap[i] & 0xf) + 0x30
            asc.append(b)
            
        return str(asc)


class Iso8583Field:
    def __init__(self):
        self.var_len = None
        self.max_len = None
        self.field_type = None
        self.compress = None


class Iso8583Packager:
    '''
    classdocs
    '''

    def __init__(self, *params):
        '''
        Constructor
        '''
        self.packager = [Iso8583Field() for i in range(ISO8583_MAX_FIELD_IDX + 1)]
        if len(params) > 0:
            self.load_config(params[0])
        
    def load_config(self, filename):
        with open(filename) as f:
            for line in f:
                line = line.strip()
                if line[0:1] == "#":
                    continue
                cols = re.split("[\t ]*", line)
                fld = self.packager[int(cols[0])]
                fld.var_len = ISO8583_FIELD_SIZE_IND[cols[1]]
                fld.max_len = int(cols[2])
                fld.field_type = ISO8583_FIELD_TYPES[cols[3]]
                fld.compress = int(cols[4])
